﻿#region Imports
using System;
#endregion Imports

namespace CommonUtilities.Util
{
    public enum eNumberStyle
    {
        Indian, International
    }

    public static class NumberHelper
    {
        public static string ConvertNumToWords(int number, eNumberStyle numberStyle = eNumberStyle.Indian)
        {
            string result = null;

            switch (number.ToString().Length)
            {
                case 1:
                    result = (number == 0 ? new Zero().ToWords(number) : new OneDigit().ToWords(number));
                    break;

                case 2:
                    result = new TwoDigit().ToWords(number);
                    break;

                case 3:
                    result = new Hundred().ToWords(number);
                    break;

                case 4:
                case 5:
                    result = new Thousand().ToWords(number);
                    break;

                case 6:
                    if (numberStyle == eNumberStyle.International) result = new Thousand().ToWords(number);
                    else if (numberStyle == eNumberStyle.Indian) result = new Lakhs().ToWords(number);
                    break;

                default: throw new InvalidOperationException("Operation not defined");
            }

            return result.Trim().Replace("  ", " ").ToLower().CapitalizeThisWord(); // "and,hundred,thousand");
        }
    }

    internal interface INumToWords
    {
        string ToWords(int number);
    }

    internal class Zero : INumToWords
    {
        #region INumToWords Members
        public string ToWords(int number)
        {
            return "zero";
        }
        #endregion INumToWords Members
    }

    internal class OneDigit : INumToWords
    {
        #region INumToWords Members
        public virtual string ToWords(int number)
        {
            return ",one,two,three,four,five,six,seven,eight,nine".Split(',')[number];
        }
        #endregion INumToWords Members
    }

    internal sealed class Teens : INumToWords
    {
        #region INumToWords Members
        public string ToWords(int number)
        {
            return "eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen".Split(',')[number - 11];
        }
        #endregion INumToWords Members
    }

    internal class TwoDigit : OneDigit
    {
        #region INumToWords Members
        public override string ToWords(int number)
        {
            if (number > 10 && number < 20) return new Teens().ToWords(number);
            else
            {
                var quot = number / 10;
                var rem = number % 10;

                return (MultipleOfTen(quot) + (quot > 1 ? " " : "")).TrimStart() + base.ToWords(rem);
            }
        }
        #endregion INumToWords Members

        private static string MultipleOfTen(int quot)
        {
            return ",ten,twenty,thirty,forty,fifty,sixty,seventy,eighty,ninety".Split(',')[quot];
        }
    }

    internal class Hundred : TwoDigit
    {
        #region INumToWords Members
        public override string ToWords(int number)
        {
            var quot = number / 100;
            var rem = number % 100;

            return (quot > 0 ? new OneDigit().ToWords(quot) + " hundred" : "")
                + (quot * rem > 0 ? " and " : "")
                + (rem > 0 ? base.ToWords(rem) : "");
        }
        #endregion INumToWords Members

        private static string MultipleOfTen(int quot)
        {
            return "ten,twenty,thirty,forty,fifty,sixty,seventy,eighty,ninety".Split(',')[quot - 1];
        }
    }

    internal class Thousand : Hundred
    {
        #region INumToWords Members
        public override string ToWords(int number)
        {
            var quot = number / 1000;
            var rem = number % 1000;

            return (quot < 1 ? new OneDigit().ToWords(quot)
                        : (quot < 100 ? new TwoDigit().ToWords(quot) : new Hundred().ToWords(quot))) + " thousand"
                + (rem > 0 && rem < 100 ? " and " : " ")
                + base.ToWords(rem).Trim();
        }
        #endregion INumToWords Members

        private static string MultipleOfTen(int quot)
        {
            return "ten,twenty,thirty,forty,fifty,sixty,seventy,eighty,ninety".Split(',')[quot - 1];
        }
    }

    internal class Lakhs : Hundred
    {
        public override string ToWords(int number)
        {
            return (number / 100000) + " lakhs " + base.ToWords(number % 100000);
        }
    }
}
